package com.market.web.controller;

import java.util.List;
import java.util.Locale;

import javax.faces.application.ConfigurableNavigationHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Controller;

import com.market.domain.Role;
import com.market.domain.User;
import com.market.domain.UserActivityRecord;
import com.market.domain.transients.UserAuthentication;
import com.market.manager.UserManager;
import com.market.util.CollectionUtil;
import com.market.web.ViewController;
import com.market.web.model.LocationModel;
import com.market.web.model.RoleModel;
import com.market.web.model.UserModel;

@Controller("userController")
@Scope("session")
public class UserController extends
		GenericController<UserManager, UserModel, User> {

	@Autowired
	private RoleModel roleModel;
	@Autowired
	private LocationModel locationModel;
	@Autowired
	private EmailController emailController;
	@Autowired
	private ViewController viewController;

	@Override
	@Autowired
	protected void setModel(UserModel model) {
		this.model = model;

	}

	@Override
	@Autowired
	protected void setManager(UserManager manager) {
		this.manager = manager;
	}

	/*
	 * @Override public User getNewObject() { User user = new User();
	 * user.setIsNew(true); return user; }
	 */

	@Override
	public User saveCurrent() {
		// If password answer has not been set, set password question to null.
		if (this.getModelCurrent().getPasswordAnswer() == null
				|| this.getModelCurrent().getPasswordAnswer().isEmpty()) {
			this.getModelCurrent().setPasswordQuestion(null);
		}
		return super.saveCurrent();
	}

	/**
	 * This method do all the preparation job for register a new user;
	 * 
	 */
	public void preRegister() {
		User user = this.model.getNewObject();
		Locale locale = FacesContext.getCurrentInstance()
				.getExternalContext().getRequestLocale();
		if (locale.getLanguage().equalsIgnoreCase("zh")) {
			user.setLanguage(User.LANGUAGE.CHINESE);
		}else{
			if(locale.getLanguage().equalsIgnoreCase("fr")){
				user.setLanguage(User.LANGUAGE.FRENCH);
			}else{
				user.setLanguage(User.LANGUAGE.ENGLISH);
			}
		}
		this.setModelCurrent(user);
	}

	public String register() {
		try {
			User user = super.saveCurrent();
			emailController.sendEmail(user.getWelcomeEmail());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String outCome = "REGISTRATION SUCCEED";
		super.getNavHandler().performNavigation(outCome);
		return outCome;
	}

	/**
	 * Add all selected roles to current user And clear selected propertied
	 * after this operation finish
	 * 
	 */
	public void addSelectedRolesToCurrent() {
		User current = this.getModelCurrent();
		List<Role> selectedRoles = roleModel.getSelecteds();
		CollectionUtil<Role> util = new CollectionUtil<Role>();
		util.addAll(current.getRoles(), selectedRoles);
		util.clearSelection(selectedRoles); // Normally we have to clear
											// selected properties, after
											// operation finish
	}
	
	
	/**
	 * Remove all selected user activity record from current user, and clear selected properties
	 * after this operation finish.
	 */
	public void removeSelectedUserActivityRecordFromCurrent(){
		List<UserActivityRecord> records  = this.getModel().getCurrent().getUserActivityRecords();
		CollectionUtil<UserActivityRecord> util = new CollectionUtil<UserActivityRecord>();
		List<UserActivityRecord> unSelectedRecords = util.extractUnSelected(records);
		this.getModelCurrent().setUserActivityRecords(unSelectedRecords);
		util.clearSelection(records);
		
	}


	/**
	 * Remove all selected roles from current user And clear selected propertied
	 * after this operation finish
	 * 
	 */

	public void removeSelectedRolesFromCurrent() {
		List<Role> roles = this.getModel().getCurrent().getRoles();
		CollectionUtil<Role> util = new CollectionUtil<Role>();
		List<Role> unSelectedRoles = util.extractUnSelected(roles);
		this.getModelCurrent().setRoles(unSelectedRoles);
		util.clearSelection(roles); // Normally we have to clear selected
									// properties, after operation finish
	}

	public void updateCurrentLocation() {
		this.model.getCurrent().setDropOffLocation(locationModel.getCurrent());
	}

	/**
	 * This method try to login current user of userModel. The informations use
	 * for login is email and password, if succeed will save this user to
	 * SecurityContextHolder. Clear password before save to
	 * SecurityContextHolder
	 * 
	 * @return
	 */
	public String loginCurrentByEmail() {
		User current = this.model.getCurrent();
		ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) FacesContext
				.getCurrentInstance().getApplication().getNavigationHandler();
		if (current.getEmail() != null
				&& current.getEmail().isEmpty() == Boolean.FALSE) {			
			User user = this.manager.loginByEmail(current);
			this.model.setCurrent(user);
			if(user != null && user.getIsLogin() == Boolean.TRUE){
				this.processLogin(user);
				handler.performNavigation("LOGIN SUCCEED");
				return "LOGIN SUCCEED";
			}			
		}else{
			this.model.setCurrent(null); //If user hasn't input email,show user not exist.
		}
			
		handler.performNavigation("LOGIN FAILED");
		return "LOGIN FAILED";
	}

	/**
	 * This method try to login current user of userModel. The information use
	 * for login is telephone and password,if succeed will save this user to
	 * SecurityContextHolder, Clear password before save to
	 * SecurityContextHolder. When login with telephone,confirmPassword property
	 * of user use for input password from view.
	 * 
	 * @return
	 */

	public String loginCurrentByTelephone() {
		User current = this.model.getCurrent();
		ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) FacesContext
				.getCurrentInstance().getApplication().getNavigationHandler();
		if (current.getTelephone() != null
				&& current.getTelephone().isEmpty() == Boolean.FALSE) {
			User user = this.manager.loginByTelephone(current);
			this.model.setCurrent(user);
			if (user != null && user.getIsLogin() == Boolean.TRUE) {
				this.processLogin(user);
				handler.performNavigation("LOGIN SUCCEED");
				return "LOGIN SUCCEED";
			}
			
		}else{
			this.model.setCurrent(null);//If user has not input telephone, show account not exist.
		}
		handler.performNavigation("LOGIN FAILED");
		return "LOGIN FAILED";
	
	}

	public String activateAccount() {
		Long id = this.getModelCurrent().getId();
		String activatedCode = this.getModelCurrent().getActivateCode();
		User user = this.manager.activateAccount(id, activatedCode);
		if (user != null) {
			this.processLogin(user);
			this.model.setCurrent(user);  //When show activation result page, use current user's isActivated property to show result.
			return "ACTIVATED SUCCEED";
		}
		this.setModelCurrent(this.getModel().getNewObject());
		return "ACTIVATED FAILED";
	}

	private void processLogin(User user) {
//		user.setIsLogin(true);
		// user.setPassword("");
		FacesContext.getCurrentInstance().getViewRoot()
				.setLocale(user.getLanguage().getLocale());
//		this.model.setCurrent(user);
		UserAuthentication userAu = new UserAuthentication(user);
		SecurityContextHolder.getContext().setAuthentication(userAu);
	}

	public String logOutCurrent() {
		User current = this.model.getCurrent();
		current.setIsLogin(false);
		SecurityContextHolder.getContext().setAuthentication(null);
		FacesContext facesContext = FacesContext.getCurrentInstance();
		ExternalContext externalContext = facesContext.getExternalContext();
		externalContext.invalidateSession();
		ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) FacesContext
				.getCurrentInstance().getApplication().getNavigationHandler();
		handler.performNavigation("LOGOUT");
		return "LOGOUT";
	}

	/**
	 * When user choose a different language,during registration or editing user
	 * info. This new language should be reflect to view.
	 */
	public void languageChanged() {
		if (this.model.getAuthenticatedUser() != null) {
			viewController.setLanguage(this.model.getAuthenticatedUser()
					.getLanguage());
		} else {
			viewController.setLanguage(this.getModelCurrent().getLanguage());
		}
	}

	/**
	 * Use this method when editing user info .
	 */
	public String storeAuthenticatedUserInfo() {
		this.manager.store(this.model.getAuthenticatedUser());
		return "UPDATE MY INFO SUCCEED";
	}

	/**
	 * Use this method when e change password.
	 */
	public String storeAuthenticatedUserPassword() {
		this.manager.store(this.model.getAuthenticatedUser());
		return "CHANGE PASSWORD SUCCEED";
	}
	
	public String changeAuthenticateUserPassword(){
		User authenticatedUser = this.getModel().getAuthenticatedUser();
		this.getModel().getAuthenticatedUser().setPassword(authenticatedUser.getNewPassword());
		this.storeAuthenticatedUserPassword();
		return "CHANGE PASSWORD SUCCEED";
	}

}
